#include "stdafx.h"
#include "IndexBuffer.h"

namespace Reign
{namespace Video
{namespace D3D10
{
	#pragma region Constructors
	IndexBuffer::IndexBuffer(DisposableI^ parent, BufferUsages bufferUsage)
	: IndexBufferI(parent, bufferUsage)
	{
		init(parent, nullptr);
	}

	IndexBuffer::IndexBuffer(DisposableI^ parent, BufferUsages bufferUsage, array<int>^ indices)
	: IndexBufferI(parent, bufferUsage)
	{
		init(parent, indices);
	}

	IndexBuffer::IndexBuffer(DisposableI^ parent, BufferUsages bufferUsage, array<int>^ indices, bool _32BitIndices)
	: IndexBufferI(parent, bufferUsage, _32BitIndices)
	{
		init(parent, indices);
	}

	void IndexBuffer::init(DisposableI^ parent, array<int>^ indices)
	{
		null();
		try
		{
			video = parent->FindParentOrSelfWithException<Video^>();
			if (indices) Init(indices);
		}
		catch (Exception^ ex)
		{
			delete this;
			throw ex;
		}
	}

	IndexBuffer::~IndexBuffer()
	{
		disposeChilderen();
		if (indexBuffer) indexBuffer->Release();
		null();
	}

	void IndexBuffer::null()
	{
		indexBuffer = 0;
		video = nullptr;
	}
	#pragma endregion

	#pragma region Methods
	void IndexBuffer::Init(array<int>^ indices)
	{
		IndexBufferI::Init(indices);
		if (indexBuffer)
		{
			indexBuffer->Release();
			indexBuffer = 0;
		}

		uint bufferSize = indexCount * indexByteSize;
		D3D10_BUFFER_DESC desc;
		ZeroMemory(&desc, sizeof(D3D10_BUFFER_DESC));
		desc.Usage = (usage == BufferUsages::Write) ? D3D10_USAGE_DYNAMIC : D3D10_USAGE_DEFAULT;
		desc.CPUAccessFlags = (usage == BufferUsages::Write) ? D3D10_CPU_ACCESS_WRITE : 0;
		desc.ByteWidth = bufferSize;
		desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
		desc.MiscFlags = 0;

		D3D10_SUBRESOURCE_DATA InitData;
		ZeroMemory(&InitData, sizeof(D3D10_SUBRESOURCE_DATA));
		if (_32BitIndices)
		{
			pin_ptr<int> indicesTEMP = &indices[0];
			InitData.pSysMem = indicesTEMP;
		}
		else
		{
			short* _16BitIndices = new short[indices->Length];
			for (int i = 0; i != indices->Length; ++i)
			{
				_16BitIndices[i] = indices[i];
			}
			InitData.pSysMem = _16BitIndices;
		}

		ID3D10Buffer* indexBufferTEMP;
		if (FAILED(video->Device->CreateBuffer(&desc, &InitData, &indexBufferTEMP)))
		{
			if (!_32BitIndices) delete InitData.pSysMem;
			Debug::ThrowError(L"IndexBuffer", L"Failed to create IndexBuffer");
		}
		indexBuffer = indexBufferTEMP;

		if (!_32BitIndices) delete InitData.pSysMem;
	}

	void IndexBuffer::Update(array<int>^ indices, int updateCount)
	{
		void* data = 0;
		indexBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &data);

		if (_32BitIndices)
		{
			pin_ptr<int> indicesTEMP = &indices[0];
			memcpy(data, indicesTEMP, indexByteSize * updateCount);
		}
		else
		{
			short* _16BitIndices = new short[updateCount];
			for (int i = 0; i != updateCount; ++i)
			{
				_16BitIndices[i] = indices[i];
			}
			memcpy(data, _16BitIndices, indexByteSize * updateCount);
		}

		indexBuffer->Unmap();
	}

	void IndexBuffer::enable()
	{
		video->Device->IASetIndexBuffer(indexBuffer, _32BitIndices ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT, 0);
	}
	#pragma endregion
}
}
}